/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package topka;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.LayoutManager;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.text.NumberFormat;
import java.util.Random;
import javax.swing.JPanel;

/**
 *
 * @author minaev
 */
public class JSlidePlotter extends JPanel {

    int MARGIN_LEFT = 10;
    int MARGIN_TOP = 10;
    int MARGIN_RIGHT = 10;
    int MARGIN_BOTTOM = 10;
    int clear_height;
    int clear_width;
    int len_x_visible = 100;
    long cnt_add_values = 0;
    double max_y = 1;
    double min_y = 0;
    public int cursor_value = 0;
    final int size_buf_values = 10000;
    public double values[] = new double[size_buf_values];
    double offset_copy_error;
    int offset_copy_error_r;
    Graphics gr = null;
    Color bg_color = Color.WHITE;
    Color graf_color = Color.RED;
    Color grid_color = Color.BLUE;
    Color grid_goriz_color = Color.MAGENTA;
    boolean drawGrid = true;
    double discretX = 1;
    int gridSteps = 50;
    boolean draw_dig = false;
    NumberFormat nform;

    private void initSlidePlotter() {
        clear_height = getClearHeight();
        clear_width = getClearWidth();
        gr = this.getGraphics();
        nform = NumberFormat.getInstance();
        nform.setMaximumFractionDigits(2);
        nform.setMinimumFractionDigits(0);
    }

    public JSlidePlotter() {
        super();
        initSlidePlotter();
    }

    public JSlidePlotter(boolean isDoubleBuffered) {
        super(isDoubleBuffered);
        initSlidePlotter();
    }

    public JSlidePlotter(LayoutManager layout) {
        super(layout);
        initSlidePlotter();
    }

    public JSlidePlotter(LayoutManager layout, boolean isDoubleBuffered) {
        super(layout, isDoubleBuffered);
        initSlidePlotter();
    }

    private int getClearHeight() {
        return this.getHeight() - MARGIN_TOP - MARGIN_BOTTOM;
    }

    private int getClearWidth() {
        return this.getWidth() - MARGIN_LEFT - MARGIN_RIGHT;
    }

    private double getOffsetCopy() {
        return clear_height / len_x_visible;
    }

    private int getY(double val) {
        return this.getHeight() - (int) ((val - min_y) * clear_height / (max_y - min_y)) - MARGIN_BOTTOM;
    }

    private int getX(int num) {
        return num * clear_width / len_x_visible + MARGIN_LEFT;
    }

    public void addValue(double val) {
        cnt_add_values++;
        clear_height = getClearHeight();
        clear_width = getClearWidth();
        gr = this.getGraphics();
        //Random r = new Random();
        //gr.fillRect(r.nextInt(clear_width),r.nextInt(clear_height),
        //           r.nextInt(clear_width), r.nextInt(clear_height));
        if (val > max_y) {
            max_y = val;
            clear_height = getClearHeight();
            clear_width = getClearWidth();
            this.update(gr);
        }
        if (val < min_y) {
            min_y = val;
            clear_height = getClearHeight();
            clear_width = getClearWidth();
            this.update(gr);
        }

        offset_copy_error += getOffsetCopy();
        offset_copy_error_r = (int) offset_copy_error;
        if (offset_copy_error_r > 1) {
            gr.copyArea(MARGIN_LEFT, MARGIN_TOP, clear_width, clear_height,
                    -offset_copy_error_r, 0);
            offset_copy_error -= offset_copy_error_r;


            gr.setColor(bg_color);
            gr.fillRect(MARGIN_LEFT - offset_copy_error_r, MARGIN_TOP,
                    offset_copy_error_r, clear_height);
            gr.fillRect(MARGIN_LEFT + clear_width - offset_copy_error_r, MARGIN_TOP,
                    offset_copy_error_r, clear_height);


        }

        gr.setColor(graf_color);
        gr.drawLine(MARGIN_LEFT + clear_width - offset_copy_error_r - 1,
                getY(getValue(len_x_visible - 2)),
                MARGIN_LEFT + clear_width,
                getY(getValue(len_x_visible - 1)));

        if (offset_copy_error_r > 1) {
            gr.setColor(bg_color);
            gr.drawLine(MARGIN_LEFT + clear_width,
                    MARGIN_TOP,
                    MARGIN_LEFT + clear_width,
                    clear_height + MARGIN_TOP);
        }

        drawGrid(gr);

        values[cursor_value] = val;
        cursor_value++;

        if (cursor_value >= size_buf_values) {
            cursor_value = 0;
        }

    }
    int tmp_ind = 0;

    private double getValue(int ind) {
        tmp_ind = cursor_value - len_x_visible + ind;
        if (tmp_ind < 0) {
            tmp_ind = size_buf_values + tmp_ind;
        }
        return values[tmp_ind];
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);

        clear_height = getClearHeight();
        clear_width = getClearWidth();

        g.setColor(bg_color);
        g.fillRect(MARGIN_LEFT, MARGIN_TOP, clear_width, clear_height);

        /*g.setColor(graf_color);
         for (int i = 0; i < len_x_visible - 1; i++) {
         g.drawLine(getX(i), getY(getValue(i)), getX(i + 1), getY(getValue(i + 1)));
         }*/
    }

    public void setDrawGrid(boolean drawGrid) {
        this.drawGrid = drawGrid;
    }

    private void drawGrid(Graphics gr) {
        if (drawGrid) {
            gr.setColor(grid_color);

            int bufi;


            if (offset_copy_error_r > 1) {
                //draw 0
                if (max_y > 0 && min_y < 0) {
                    bufi = getY(0);
                    gr.drawLine(MARGIN_LEFT + clear_width - offset_copy_error_r - 1,
                            bufi,
                            MARGIN_LEFT + clear_width,
                            bufi);
                    gr.drawLine(MARGIN_LEFT + clear_width - offset_copy_error_r - 1,
                            bufi + 1,
                            MARGIN_LEFT + clear_width,
                            bufi + 1);
                }

                //graw goriz

                if (cnt_add_values % (gridSteps * 4) == gridSteps * 2.5) {
                    draw_dig = true;
                }
                if (cnt_add_values % 10 > 5) {
                    gr.setColor(grid_goriz_color);
                    double grd_step = getStepGrid(min_y, max_y);
                    double grd_offset = getOffsetGrid(min_y, grd_step);
                    /*int grd_offset_i = getY(grd_offset);
                     int grd_step_i = grd_offset_i - getY(grd_offset + grd_step);*/
                    if (grd_step > 0) {
                        while (grd_offset < max_y) {
                            bufi = getY(grd_offset);
                            gr.drawLine(MARGIN_LEFT + clear_width - offset_copy_error_r - 1,
                                    bufi,
                                    MARGIN_LEFT + clear_width - offset_copy_error_r,
                                    bufi);
                            grd_offset += grd_step;
                            if (draw_dig) {
                                gr.drawString(nform.format(grd_offset),
                                        MARGIN_LEFT + clear_width
                                        - gr.getFontMetrics().stringWidth(nform.
                                        format(grd_offset)) - 5,
                                        bufi + 10);
                            }
                        }
                        draw_dig = false;
                    }
                    gr.setColor(grid_color);
                }
            }
            //draw vert lines
            if (cnt_add_values % gridSteps == 0) {
                bufi = MARGIN_TOP;
                while (bufi < clear_height + MARGIN_TOP - 5) {
                    gr.drawLine(MARGIN_LEFT + clear_width - offset_copy_error_r - 1,
                            bufi,
                            MARGIN_LEFT + clear_width - offset_copy_error_r - 1,
                            bufi + 5);
                    bufi += 10;
                }
                gr.drawString(nform.format(cnt_add_values * discretX),
                        MARGIN_LEFT + clear_width
                        - gr.getFontMetrics().stringWidth(
                        nform.format(cnt_add_values * discretX)) - 5,
                        MARGIN_TOP + clear_height - 5);
            }



        }
    }

    public static double getStepGrid(double start, double end) {
        double res = (end - start) / 10;
        int cnt_div = 0;
        boolean isdiv;
        if (res <= 0) {
            return 0;
        }
        isdiv = (res >= 1);

        while ((isdiv && (res > 1)) || (!isdiv && res < 1)) {
            if (isdiv) {
                res /= 10;
                cnt_div++;
            } else {
                res *= 10;
                cnt_div--;
            }
        }
        res = (end - start) / Math.pow(10, cnt_div);
        if (res > 10) {
            res = 2;
        } else if (res < 2) {
            res = 0.5;
        } else {
            res = 1;
        }
        return Math.pow(10, cnt_div) * res;
    }

    public static double getOffsetGrid(double start, double step) {
        return start + (step - (start + step) % step);
    }

    public void setDiscretX(double discretX) {
        if (discretX > 0) {
            this.discretX = discretX;
        }
    }

    public void resetXValues() {
        this.cnt_add_values = 0;
    }

    public void setGridSteps(int gridSteps) {
        if (gridSteps > 0) {
            this.gridSteps = gridSteps;
        }
    }
}
